package com.mxb.sqlsession;

import com.mxb.model.Configuration;
import com.mxb.model.MappedStatement;

import java.lang.reflect.*;
import java.util.List;
import java.util.Map;

/**
 * SqlSession实现类
 *
 * @Author: MaXiaobing
 * @Date: 2021/3/9 17:53
 */
public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;

    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public int update(MappedStatement mappedStatement, Object... params) throws Exception {
        SimpleExecutor simpleExecutor = new SimpleExecutor();
        int rows = simpleExecutor.update(configuration, mappedStatement, params);
        return rows;
    }

    @Override
    public <T> List<T> selectList(String statementId, Object... params) throws Exception {
        // 调用SimpleExecutor中的query方法
        SimpleExecutor simpleExecutor = new SimpleExecutor();
        Map<String, MappedStatement> mappedStatementMap = configuration.getMappedStatementMap();
        MappedStatement mappedStatement = mappedStatementMap.get(statementId);
        List<Object> list = simpleExecutor.query(configuration, mappedStatement, params);
        return (List<T>) list;
    }

    @Override
    public <T> T selectOne(String statementId, Object... params) throws Exception {
        List<Object> objects = selectList(statementId, params);
        if (objects.size() == 1) {
            return (T) objects.get(0);
        } else {
            throw new RuntimeException("查询结果为空或返回结果过多！");
        }
    }

    @Override
    public <T> T getMapper(Class<?> mapperClass) {
        // 使用jdk动态代理为Dao接口生成代理对象
        Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass},
            new InvocationHandler() {
                /**
                 *
                 * @param proxy 当前被代理对象的引用
                 * @param method 当前被调用方法的引用
                 * @param args 传递的参数
                 * @return
                 * @throws Throwable
                 */
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 根据不同情况，自动调用selectOne和selectList
                    // 1、准备参数1，statementId = namespace.id，也就是接口全限定名.方法名
                    String className = method.getDeclaringClass().getName();
                    String methodName = method.getName();
                    String statementId = className + "." + methodName;
                    // 2、准备参数2，params
                    // 获取commandType（sql类型），据此区分CURD操作
                    Map<String, MappedStatement> mappedStatementMap = configuration.getMappedStatementMap();
                    MappedStatement mappedStatement = mappedStatementMap.get(statementId);
                    String commandType = mappedStatement.getCommandType();

                    switch (commandType) {
                        case "insert":
                        case "update":
                        case "delete": {
                            return update(mappedStatement, args);
                        }
                        case "select": {
                            Type genericReturnType = method.getGenericReturnType();
                            if (genericReturnType instanceof ParameterizedType) {
                                List<Object> objectList = selectList(statementId, args);
                                return objectList;
                            } else {
                                return selectOne(statementId, args);
                            }
                        }
                        default:
                            throw new Exception("Unknown execution method for: " + commandType);
                    }
                }
            });
        return (T) proxyInstance;
    }

}
